<!DOCTYPE html>
<html class="no-js" lang="zh">
  <head>
<meta charset="utf-8">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<link rel="alternate" href="https://www.desgard.com" hreflang="pt-BR">
<link href="http://gmpg.org/xfn/11" rel="profile">
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="theme-color" content="#005344">
<title>Guardia · 瓜地</title>
<meta name="google-site-verification" content="zS1dSn20XtA4FJYEOQLXqI0boxZdMnJ2g3beje-cl20">
<meta name="description" content="I write many code to write less code.💻">
<meta name="keywords" content="">
<!-- Social: Facebook / Open Graph -->
<meta property="og:url" content="https://www.desgard.com/isa/">
<meta property="og:title" content="       用 isa 承载对象的类信息 | Gua  ">
<meta property="og:description" content="I write many code to write less code.💻">
<meta property="og:site_name" content="Desgard_Duan">
<meta property="og:locale" content="pt_BR">
<meta property="og:type" content="website">
<meta property="og:author" content="https://www.facebook.com/desgard.duan">
<meta property="og:image" content="https://www.desgard.com">
<!-- Social: Twitter -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@nandomoreirame">
<meta name="twitter:domain" content="https://www.desgard.com">
<meta name="twitter:title" content="       用 isa 承载对象的类信息 | Gua  ">
<meta name="twitter:description" content="I write many code to write less code.💻">
<meta name="twitter:image:src" content="https://www.desgard.com">
<!-- Favicons -->
<link rel="apple-touch-icon" sizes="114x114" href="https://www.desgard.com/assets/ico/apple-touch-icon-114-516f4e19976b9e4dbb77ad9b576831fe.png">
<link rel="apple-touch-icon" sizes="72x72" href="https://www.desgard.com/assets/ico/apple-touch-icon-72-5409b2df229305703caf583d86c845ab.png">
<link rel="apple-touch-icon" href="https://www.desgard.com/assets/ico/apple-touch-icon-57-aa873e019cf659e0d4e6a0b5bb9f379d.png">
<link rel="shortcut icon" href="https://www.desgard.com/assets/ico/favicon-4298be3d3fbe23e18d65bded9d930899.png">
<!-- rel prev and next -->
<!-- Canonical link tag -->
<link rel="canonical" href="https://www.desgard.com/isa/">
<link rel="alternate" type="application/rss+xml" title="Gua" href="https://www.desgard.com/feed.xml">
<link rel="stylesheet" href="/assets/main-0b7b828712f4c43b75bba4861c907bb1.css">
<script src="/assets/modernizr-custom-cb807611a3e262b2eac59444cbab74d6.js" data-cfasync="false"></script>
<script src="/assets/js/jquery-3.1.js"></script>
<script src="/assets/js/jquery.rotate.js"></script>
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "Website",
  "publisher": "www.desgard.com",
  "url": "http://www.desgard.com/",
  "description": "瓜地"
}
</script>
<script type="text/javascript">
  var disqus_shortname = 'desgard',
      baseurl          = '';
</script>
<!--
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-52446115-1']);
_gaq.push(['_trackPageview']);
(function() {
  var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
  ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
  var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>-->
<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "//hm.baidu.com/hm.js?c5a8123bc51782a3083a631ed9ff50e4";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script>
<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
      tex2jax: {
        inlineMath: [ ['$','$'] ],
        displayMath: [ ['$$','$$'] ],
        processEscapes: true
      }
    });
  </script>
  </head>
  <body class="post-template single">
    <header class="header">
  <div class="container">
    <h1><a href="/"><strong>desgard</strong>.com</a></h1>
    <nav class="navbar">
      <ul>
        <li><a href="/">Home</a></li>
        <li><a href="#!" data-modal="modalSearch"><i class="fa fa-search"></i></a></li>
        <li><a href="/comment " target="_blank"><i class="fa fa-comments"></i></a></li>
        <li><a href="/feed.xml" target="_blank"><i class="fa fa-feed"></i></a></li>
      </ul>
    </nav>
  </div>
</header>
    <main class="wrapper container" itemprop="mainContentOfPage" itemscope="itemscope" itemtype="http://schema.org/Blog">
      <article class="post" itemscope="itemscope" itemtype="http://schema.org/BlogPosting" itemprop="blogPost">
  <header class="post-header">
    <h1>用 isa 承载对象的类信息</h1>
    <div class="post-meta">
      <time datetime="2015-10-13">
        <i class="fa fa-calendar-o"></i> <time datetime="2016-09-30"> 2016-09-30
      </time>
      <span>
        <i class="fa fa-comments"></i> <a href="#container">Comment me!</a>
      </span>
      <span>
<!--
        <i class="fa fa-folder-open-o"></i> 
-->
      </span>
    </div>
  </header>
  <p><em>Effective Objective-C 2.0 - 52 Specific Ways to Improve Your iOS and OS X Programs</em> 一书中，<em>tip 14</em> 中提到了，<em>运行时检查对象类型</em> 的 <strong>自省</strong> (Introspection) 特性。那么先来说说 <strong>自省</strong> 和 <strong>反射</strong> 的定义是什么。</p>
<h2 id="section">自省与反射的简单认识</h2>
<p>第一次听说这两个概念，是在 <em>Thinking in Java (4th Edition)</em> 中的，而深入学习他们则是在 Python 语言的学习中，以下我用 Python 来举例说明。</p>
<blockquote>
  <p>wikipedia: In computer science, reflection is the ability of a computer program to examine and modify its own structure and behavior (specifically the values, meta-data, properties and functions) at runtime.</p>
</blockquote>
<p>反射(Reflection) 是指计算机程序可以在运行时动态监测并修改它自己的结构和行为，比如值、元数据、属性和函数等的能力。通过反射，可以在运行时动态监测、生成、修改自己实际执行的等效代码。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">HelloClass</span><span class="p">(</span><span class="n">object</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="nb">method</span><span class="p">):</span>
        <span class="nb">self</span><span class="o">.</span><span class="n">method</span> <span class="o">=</span> <span class="nb">method</span>
        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;You are calling me from &#39;</span> <span class="o">+</span> <span class="nb">self</span><span class="o">.</span><span class="n">method</span><span class="p">)</span>
    <span class="k">def</span> <span class="nf">say_hello</span><span class="p">(</span><span class="nb">self</span><span class="p">):</span>
        <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Hello -- From: &quot;</span> <span class="o">+</span> <span class="nb">self</span><span class="o">.</span><span class="n">method</span><span class="p">)</span>
        <span class="nb">print</span><span class="p">()</span>
<span class="c1"># Normal</span>
<span class="n">obj</span> <span class="o">=</span> <span class="no">HelloClass</span><span class="p">(</span><span class="s1">&#39;Normal&#39;</span><span class="p">)</span>
<span class="n">obj</span><span class="o">.</span><span class="n">say_hello</span><span class="p">()</span>
<span class="c1"># Reflection</span>
<span class="n">class_name</span> <span class="o">=</span> <span class="s2">&quot;HelloClass&quot;</span>
<span class="nb">method</span> <span class="o">=</span> <span class="s2">&quot;say_hello&quot;</span>
<span class="n">obj</span> <span class="o">=</span> <span class="n">globals</span><span class="p">()</span><span class="o">[</span><span class="n">class_name</span><span class="o">]</span><span class="p">(</span><span class="s1">&#39;Reflection&#39;</span><span class="p">)</span>
<span class="n">getattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="nb">method</span><span class="p">)()</span></code></pre></div>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">You</span> <span class="n">are</span> <span class="n">calling</span> <span class="n">me</span> <span class="n">from</span> <span class="no">Normal</span>
<span class="no">Hello</span> <span class="o">--</span> <span class="ss">From</span><span class="p">:</span> <span class="no">Normal</span>
<span class="p">()</span>
<span class="no">You</span> <span class="n">are</span> <span class="n">calling</span> <span class="n">me</span> <span class="n">from</span> <span class="no">Reflection</span>
<span class="no">Hello</span> <span class="o">--</span> <span class="ss">From</span><span class="p">:</span> <span class="no">Reflection</span>
<span class="p">()</span></code></pre></div>
<p>两种方法可以达到同样的效果。但是，第一种方法是我们所说的常规方法，创建 HelloClass 这个 class 的一个实例，然后调用其中的方法。第二种我们用到了反射机制，通过 <code>globals()</code> 这个字典中来查找 <code>HelloClass</code> 这个类，并加以参数进行实例化于 <code>obj</code>，之后通过 <code>getattr</code> 函数获得 <code>say_hello</code> 方法，传参调用。</p>
<p>反射的好处在于，<code>class_name</code> 和 <code>method</code> 变量的值可以在运行时获取或者修改，这样可以动态地改变程序的行为。</p>
<blockquote>
  <p>wikipedia: In computing, type introspection is the ability of a program to examine the type or properties of an object at runtime.</p>
</blockquote>
<p>自省(Introspection) 是程序在运行时检测自己的某个对象的类型或者属性、方法的能力。例如在 Python 中的 <code>dir</code> 方法。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">HelloClass</span><span class="p">(</span><span class="n">object</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="nb">method</span><span class="p">):</span>
        <span class="nb">self</span><span class="o">.</span><span class="n">method</span> <span class="o">=</span> <span class="nb">method</span>
        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;You are calling me from &#39;</span> <span class="o">+</span> <span class="nb">self</span><span class="o">.</span><span class="n">method</span><span class="p">)</span>
    <span class="k">def</span> <span class="nf">say_hello</span><span class="p">(</span><span class="nb">self</span><span class="p">):</span>
        <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Hello -- From: &quot;</span> <span class="o">+</span> <span class="nb">self</span><span class="o">.</span><span class="n">method</span><span class="p">)</span>
        <span class="nb">print</span><span class="p">()</span>
<span class="n">obj</span> <span class="o">=</span> <span class="no">HelloClass</span><span class="p">(</span><span class="s1">&#39;Normal&#39;</span><span class="p">)</span>
<span class="n">obj_msg</span> <span class="o">=</span> <span class="n">dir</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="k">for</span> <span class="n">x</span> <span class="k">in</span> <span class="ss">obj_msg</span><span class="p">:</span>
    <span class="nb">print</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span></code></pre></div>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">__class__</span>
<span class="n">__delattr__</span>
<span class="n">__dict__</span>
<span class="n">__doc__</span>
<span class="n">__format__</span>
<span class="n">__getattribute__</span>
<span class="n">__hash__</span>
<span class="n">__init__</span>
<span class="n">__module__</span>
<span class="n">__new__</span>
<span class="n">__reduce__</span>
<span class="n">__reduce_ex__</span>
<span class="n">__repr__</span>
<span class="n">__setattr__</span>
<span class="n">__sizeof__</span>
<span class="n">__str__</span>
<span class="n">__subclasshook__</span>
<span class="n">__weakref__</span>
<span class="nb">method</span>
<span class="n">say_hello</span></code></pre></div>
<p>通过 <code>dir()</code> 函数从而做到自省，它可以返回某个对象的所有属性、方法等列表。</p>
<p>通过上述简单描述，我们大概知道了反射其实是包含着自省能力的，不仅可以获取到对象的各种属性信息，而且还可以动态修改自身的结构和行为。</p>
<h2 id="objcclass-">objc_class 结构</h2>
<p>在 ObjC 中，也支持在运行时检查对象类型这一操作，并且这个特性是内置于 Foundation 框架的 NSObject 协议中的。凡是公共基类(Common Root Class)，即 NSObject 或 NSProxy ，继承而来的对象都要遵循此协议。</p>
<p>虽然 ObjC 支持自省这一特性，就一定会对 Class 信息做存储。这里我们便要引出 isa 指针。倘若对 ObjC 有一定的学习基础，都会知道 <strong>Objective-C 对象都可以通过 clang 进行 c 的语法格式转换，从而以 struct 来描述</strong>。所有的对象中都有一个 <code>isa</code> 指针，其含义是： it <strong>is a</strong> object! 而在最新的 runtime 库中，其 isa 指针的结构已经发生了变化。</p>
<p>以下代码均参考 runtime 版本为 <a href="http://opensource.apple.com/tarballs/objc4/">objc4-680.tar.gz</a>。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">struct</span> <span class="n">objc_object</span> <span class="p">{</span>
<span class="kp">private</span><span class="p">:</span>
    <span class="n">isa_t</span> <span class="n">isa</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>会发现在 objc_object 这个基类中只有一个成员，即 isa_t 联合体(union) 类型的 isa 成员。而对于类对象的定义，可以从 objc_class 查看其结构：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">struct</span> <span class="n">objc_class</span> <span class="p">:</span> <span class="n">objc_object</span> <span class="p">{</span>
    <span class="sr">//</span> <span class="no">Class</span> <span class="no">ISA</span><span class="p">;</span>
    <span class="no">Class</span> <span class="n">superclass</span><span class="p">;</span> 	<span class="sr">//</span> <span class="err">父类引用</span>
    <span class="n">cache_t</span> <span class="n">cache</span><span class="p">;</span>		<span class="sr">//</span> <span class="err">用来缓存指针和虚函数表</span>
    <span class="n">class_data_bits_t</span> <span class="n">bits</span><span class="p">;</span> <span class="sr">//</span> <span class="n">class_rw_t</span> <span class="err">指针加上</span> <span class="n">rr</span><span class="o">/</span><span class="n">alloc</span> <span class="err">标志</span>
<span class="p">}</span></code></pre></div>
<blockquote>
  <p>runtime 的开源作者怕学习者不知道 isa 已经从 objc_object 继承存在，用注释加以提示。</p>
</blockquote>
<p>其实，开发中所使用的类和实例，都会拥有一个记录自身信息的 isa 指针，只是因为 runtime 从 objc_object 继承出的，所以不会显式看到。</p>
<p><img src="http://7xwh85.com1.z0.glb.clouddn.com/objc_object_structure.png" alt="isa" /></p>
<p>需要知道的是，class_data_bits_t 中存有 Class 的对应方法，具体如何存储，会在后续的文中记录。</p>
<h2 id="isa-">isa 优化下的信息记录</h2>
<p>isa 是一个联合体类型，其结构如下：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">union</span> <span class="n">isa_t</span> <span class="p">{</span>
    <span class="n">isa_t</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
    <span class="n">isa_t</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">value</span><span class="p">)</span> <span class="p">:</span> <span class="n">bits</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
    <span class="no">Class</span> <span class="n">cls</span><span class="p">;</span>
    <span class="n">uintptr_t</span> <span class="n">bits</span><span class="p">;</span>
    <span class="n">struct</span> <span class="p">{</span>
        <span class="n">uintptr_t</span> <span class="n">indexed</span>           <span class="p">:</span> <span class="mi">1</span><span class="p">;</span>
        <span class="n">uintptr_t</span> <span class="n">has_assoc</span>         <span class="p">:</span> <span class="mi">1</span><span class="p">;</span>
        <span class="n">uintptr_t</span> <span class="n">has_cxx_dtor</span>      <span class="p">:</span> <span class="mi">1</span><span class="p">;</span>
        <span class="n">uintptr_t</span> <span class="n">shiftcls</span>          <span class="p">:</span> <span class="mi">33</span><span class="p">;</span> 
        <span class="n">uintptr_t</span> <span class="n">magic</span>             <span class="p">:</span> <span class="mi">6</span><span class="p">;</span>
        <span class="n">uintptr_t</span> <span class="n">weakly_referenced</span> <span class="p">:</span> <span class="mi">1</span><span class="p">;</span>
        <span class="n">uintptr_t</span> <span class="n">deallocating</span>      <span class="p">:</span> <span class="mi">1</span><span class="p">;</span>
        <span class="n">uintptr_t</span> <span class="n">has_sidetable_rc</span>  <span class="p">:</span> <span class="mi">1</span><span class="p">;</span>
        <span class="n">uintptr_t</span> <span class="n">extra_rc</span>          <span class="p">:</span> <span class="mi">19</span><span class="p">;</span>
    <span class="p">};</span>
<span class="p">};</span></code></pre></div>
<blockquote>
  <p>该定义是在 <code>__arm64__</code> 环境下的 isa_t 联合体结构。因为 iOS 应用为 <code>__arm64__</code> 架构环境。</p>
</blockquote>
<p><strong>可以看到在 isa_t 联合体中不仅仅表明了指向对象的地址信息，而且这个 64 位数据还记录了其 bits 情况以及该实例每一位保存的对象信息</strong>。来验证一下（记住要使用真机调试， real device 和 simulator 的架构环境是有一定区别）：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span><span class="n">viewDidLoad</span> <span class="p">{</span>
    <span class="no">NSObject</span> <span class="o">*</span><span class="n">object</span> <span class="o">=</span> <span class="o">[</span><span class="no">NSObject</span> <span class="kp">new</span><span class="o">]</span><span class="p">;</span>
    <span class="sr">//</span> <span class="err">在</span> <span class="no">ARC</span> <span class="err">模式下，通过</span> <span class="n">__bridge</span> <span class="err">转换</span> <span class="nb">id</span> <span class="err">类型为</span> <span class="p">(</span><span class="n">void</span> <span class="o">*</span><span class="p">)</span> <span class="err">类型</span>
    <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;isa: %p &quot;</span><span class="p">,</span> <span class="o">*</span><span class="p">(</span><span class="n">void</span> <span class="o">**</span><span class="p">)(</span><span class="n">__bridge</span> <span class="n">void</span> <span class="o">*</span><span class="p">)</span><span class="n">object</span><span class="p">);</span>
    <span class="n">static</span> <span class="n">void</span> <span class="o">*</span><span class="n">someKey</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">someKey</span><span class="p">;</span>
    <span class="n">objc_setAssociatedObject</span><span class="p">(</span><span class="n">object</span><span class="p">,</span> <span class="n">someKey</span><span class="p">,</span> <span class="err">@</span><span class="s2">&quot;Desgard_Duan&quot;</span><span class="p">,</span> <span class="no">OBJC_ASSOCIATION_RETAIN</span><span class="p">);</span>
    <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;isa: %p &quot;</span><span class="p">,</span> <span class="o">*</span><span class="p">(</span><span class="n">void</span> <span class="o">**</span><span class="p">)(</span><span class="n">__bridge</span> <span class="n">void</span> <span class="o">*</span><span class="p">)</span><span class="n">object</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p>输出结果为：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="mi">2016</span><span class="o">-</span><span class="mi">09</span><span class="o">-</span><span class="mi">25</span> <span class="mi">23</span><span class="p">:</span><span class="mo">01</span><span class="p">:</span><span class="mi">44</span><span class="o">.</span><span class="mi">257</span> <span class="ss">isa</span><span class="p">:</span> <span class="mh">0x1a1ae5a3ea1</span> 
<span class="mi">2016</span><span class="o">-</span><span class="mi">09</span><span class="o">-</span><span class="mi">25</span> <span class="mi">23</span><span class="p">:</span><span class="mo">01</span><span class="p">:</span><span class="mi">44</span><span class="o">.</span><span class="mi">257</span> <span class="ss">isa</span><span class="p">:</span> <span class="mh">0x1a1ae5a3ea3</span></code></pre></div>
<p>首先先来看一下这 64 个二进制位每一位的含义：</p>
<table>
  <thead>
    <tr>
      <th>区域名</th>
      <th>代表信息</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>indexed</td>
      <td>0 表示普通的 <code>isa</code> 指针，1 表示使用优化，存储引用计数</td>
    </tr>
    <tr>
      <td>has_assoc</td>
      <td>表示该对象是否包含 <code>associated object</code>，如果没有，则析构时会更快</td>
    </tr>
    <tr>
      <td>has_cxx_dtor</td>
      <td>表示该对象是否有 C++ 或 ARC 的析构函数，如果没有，则析构时更快</td>
    </tr>
    <tr>
      <td>shiftcls</td>
      <td>类的指针</td>
    </tr>
    <tr>
      <td>magic</td>
      <td>固定值，用于在调试时分辨对象是否未完成初始化</td>
    </tr>
    <tr>
      <td>weakly_referenced</td>
      <td>表示该对象是否有过 weak 对象，如果没有，则析构时更快</td>
    </tr>
    <tr>
      <td>deallocating</td>
      <td>表示该对象是否正在析构</td>
    </tr>
    <tr>
      <td>has_sidetable_rc</td>
      <td>表示该对象的引用计数值是否过大无法存储在 isa 指针</td>
    </tr>
    <tr>
      <td>extra_rc</td>
      <td>存储引用计数值减一后的结果</td>
    </tr>
  </tbody>
</table>
<p>将 16 进制的 <code>0x1a1ae5a3ea3</code> 转换成二进制。发现在 <code>has_assoc</code> 和 <code>index</code> 两个位都是 1 。根据代码我们可以知道我们手动为其设置了 <code>associated object</code>，所以以上的含义表是正确的。这里详细的再说一下 <code>indexed</code> 的含义。</p>
<p><img src="http://7xwh85.com1.z0.glb.clouddn.com/isa-bits.png" alt="isa-bits" /></p>
<h2 id="isa-indexed--magic-">isa 初始化行为，indexed 以及 magic 段的默认值</h2>
<p><code>isa</code> 指针会通过 <code>initIsa</code> 来初始化。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1">#define ISA_MASK        0x0000000ffffffff8ULL</span>
<span class="c1">#define ISA_MAGIC_MASK  0x000003f000000001ULL</span>
<span class="c1">#define ISA_MAGIC_VALUE 0x000001a000000001ULL</span>
<span class="n">inline</span> <span class="n">void</span>  
<span class="n">objc_object</span><span class="o">::</span><span class="n">initInstanceIsa</span><span class="p">(</span><span class="no">Class</span> <span class="n">cls</span><span class="p">,</span> <span class="n">bool</span> <span class="n">hasCxxDtor</span><span class="p">)</span>  
<span class="p">{</span>
	<span class="sr">//</span> <span class="n">initIsa</span> <span class="err">入口函数</span>
	<span class="sr">//</span> <span class="err">传入</span> <span class="no">Class</span> <span class="err">对象，是否为</span> <span class="n">isa</span> <span class="err">优化量，</span>
    <span class="n">initIsa</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="kp">true</span><span class="p">,</span> <span class="n">hasCxxDtor</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">inline</span> <span class="n">void</span>  
<span class="n">objc_object</span><span class="o">::</span><span class="n">initIsa</span><span class="p">(</span><span class="no">Class</span> <span class="n">cls</span><span class="p">,</span> <span class="n">bool</span> <span class="n">indexed</span><span class="p">,</span> <span class="n">bool</span> <span class="n">hasCxxDtor</span><span class="p">)</span>  
<span class="p">{</span> 
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">indexed</span><span class="p">)</span> <span class="p">{</span>
    	<span class="sr">//</span> <span class="err">如果没有使用</span> <span class="n">isa</span> <span class="err">优化，其内部只记录地址信息</span>
        <span class="n">isa</span><span class="o">.</span><span class="n">cls</span> <span class="o">=</span> <span class="n">cls</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    	<span class="sr">//</span> <span class="no">ISA_MAGIC_VALUE</span> <span class="err">为</span> <span class="n">bits</span><span class="err">（</span><span class="n">isa</span> <span class="err">信息）赋初值</span>
    	<span class="sr">//</span> <span class="err">注意在</span> <span class="n">arm64</span> <span class="err">下</span> <span class="n">mask</span> <span class="err">部分固定为</span> <span class="mh">0x1a</span>
        <span class="n">isa</span><span class="o">.</span><span class="n">bits</span> <span class="o">=</span> <span class="no">ISA_MAGIC_VALUE</span><span class="p">;</span>
        <span class="sr">//</span> <span class="err">是否拥有</span> <span class="n">C</span><span class="o">++</span> <span class="err">中的析构函数</span>
        <span class="n">isa</span><span class="o">.</span><span class="n">has_cxx_dtor</span> <span class="o">=</span> <span class="n">hasCxxDtor</span><span class="p">;</span>
        <span class="sr">//</span> <span class="err">由于使用了</span> <span class="n">isa</span> <span class="err">优化，所以第三位拥有其他信息</span>
        <span class="sr">//</span> <span class="err">需要将</span> <span class="n">cls</span> <span class="err">初始数据左移，保存在</span> <span class="n">shiftcls</span> <span class="err">对应位置</span>
        <span class="n">isa</span><span class="o">.</span><span class="n">shiftcls</span> <span class="o">=</span> <span class="p">(</span><span class="n">uintptr_t</span><span class="p">)</span><span class="n">cls</span> <span class="o">&gt;&gt;</span> <span class="mi">3</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>在以上代码中，可以看到在一个 <code>isa_t</code> 结构中，magic 段是一个固定值，在 arm64 架构下其值为 <code>0x1a</code>，而在 x86 下则为 <code>0x1d</code>，笔者猜测这一位也有判断架构类型之意。而观察 isa 初始化的调用栈，可以发现是 <code>callAlloc</code> 函数进行调用。这段代码的解读，将放在以后的文中。</p>
<h2 id="isa--tagged-pointer-">ISA() 获取非 Tagged Pointer 对象</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1">#define ISA_MASK        0x0000000ffffffff8ULL</span>
<span class="sr">//</span> <span class="err">简单</span> <span class="n">isa</span> <span class="err">初始化方式</span>
<span class="n">inline</span> <span class="n">void</span> 
<span class="n">objc_object</span><span class="o">::</span><span class="n">initIsa</span><span class="p">(</span><span class="no">Class</span> <span class="n">cls</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">initIsa</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="kp">false</span><span class="p">,</span> <span class="kp">false</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">inline</span> <span class="n">void</span> 
<span class="n">objc_object</span><span class="o">::</span><span class="n">initClassIsa</span><span class="p">(</span><span class="no">Class</span> <span class="n">cls</span><span class="p">)</span>
<span class="p">{</span>
	<span class="sr">//</span> <span class="n">non</span><span class="o">-</span><span class="n">pointer</span> <span class="n">isa</span> <span class="err">情况</span>
    <span class="k">if</span> <span class="p">(</span><span class="no">DisableIndexedIsa</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">initIsa</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="kp">false</span><span class="p">,</span> <span class="kp">false</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="n">initIsa</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="kp">true</span><span class="p">,</span> <span class="kp">false</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="n">inline</span> <span class="n">void</span>
<span class="n">objc_object</span><span class="o">::</span><span class="n">initProtocolIsa</span><span class="p">(</span><span class="no">Class</span> <span class="n">cls</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">return</span> <span class="n">initClassIsa</span><span class="p">(</span><span class="n">cls</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">inline</span> <span class="no">Class</span> 
<span class="n">objc_object</span><span class="o">::</span><span class="no">ISA</span><span class="p">()</span> 
<span class="p">{</span>
    <span class="n">assert</span><span class="p">(</span><span class="o">!</span><span class="n">isTaggedPointer</span><span class="p">());</span> 
    <span class="sr">//</span> <span class="err">与有效位全</span> <span class="mi">1</span> <span class="err">码进行与运算来过滤非有效位</span>
    <span class="k">return</span> <span class="p">(</span><span class="no">Class</span><span class="p">)(</span><span class="n">isa</span><span class="o">.</span><span class="n">bits</span> <span class="o">&amp;</span> <span class="no">ISA_MASK</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p><img src="http://7xwh85.com1.z0.glb.clouddn.com/ISA--.png" alt="ISA--" /></p>
<p>从中发现，其有效区域也就是 isa_t 中的 <code>shiftcls</code> 区域。而且这种掩码方式，也是从 isa_t 中查询信息的主要方式，再很多方法中可以看见类似的做法。</p>
<h2 id="isa--1">isa 的主地址检索</h2>
<p>无论在新旧版本的 Objective-C 中，都会有 isa 指针来记录类的信息。而在现在的 runtime 库中，由于 64 位的优势，使用联合体又增加了类信息记录的补充。而对于 isa 的主要部分，其记录的主要信息是什么呢？</p>
<p>在之前的一些文章中，笔者通过了 ObjC 的消息转发机制稍微提及了一些关于 isa 的知识，可以参考这篇文章 <em><a href="http://www.cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html">objc_msgSend消息传递学习笔记 - 对象方法消息传递流程</a></em> 。 在消息传递的主要流程中，最重要的一个环节就是<em>快速查询 isa 操作 GetIsaFast</em> ，其中要继续的搜寻所属 Class 的方法列表（所有成员方法所对应的 Hash Table）。可见 isa 记录的地址信息和当前实例的 Class 有直接关系。</p>
<p>下面通过实验来验证我们的猜测：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span><span class="n">viewDidLoad</span> <span class="p">{</span>
    <span class="no">NSObject</span> <span class="o">*</span><span class="n">object</span> <span class="o">=</span> <span class="o">[</span><span class="no">NSObject</span> <span class="kp">new</span><span class="o">]</span><span class="p">;</span>
    <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;isa: %p &quot;</span><span class="p">,</span> <span class="o">*</span><span class="p">(</span><span class="n">void</span> <span class="o">**</span><span class="p">)(</span><span class="n">__bridge</span> <span class="n">void</span> <span class="o">*</span><span class="p">)</span><span class="n">object</span><span class="p">);</span>
    <span class="no">NSObject</span> <span class="o">*</span><span class="n">object_2</span> <span class="o">=</span> <span class="o">[</span><span class="no">NSObject</span> <span class="kp">new</span><span class="o">]</span><span class="p">;</span>
    <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;isa: %p &quot;</span><span class="p">,</span> <span class="o">*</span><span class="p">(</span><span class="n">void</span> <span class="o">**</span><span class="p">)(</span><span class="n">__bridge</span> <span class="n">void</span> <span class="o">*</span><span class="p">)</span><span class="n">object_2</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p>在真机上运行该代码片段，可以发现其输出的结果：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="mi">2016</span><span class="o">-</span><span class="mi">09</span><span class="o">-</span><span class="mi">30</span> <span class="mi">10</span><span class="p">:</span><span class="mi">34</span><span class="p">:</span><span class="mi">15</span><span class="o">.</span><span class="mi">577813</span> <span class="ss">isa</span><span class="p">:</span> <span class="mh">0x1a1a96cbea1</span>
<span class="mi">2016</span><span class="o">-</span><span class="mi">09</span><span class="o">-</span><span class="mi">30</span> <span class="mi">10</span><span class="p">:</span><span class="mi">34</span><span class="p">:</span><span class="mi">15</span><span class="o">.</span><span class="mi">577897</span> <span class="ss">isa</span><span class="p">:</span> <span class="mh">0x1a1a96cbea1</span></code></pre></div>
<p>在输出 isa 的指针后，可以发现其记录的值完全相等。并且再通过对其 isa 指向地址的 Class Name 输出，可知其 isa 指针是指向所属 Class 对象地址。这只是对于对象实例的 isa 指针而言。</p>
<p>至此我们可能会产生另外一个疑问：</p>
<blockquote>
  <p>既然 Objective-C 将所有的事物对象化，那么其所属 Class 也会拥有 isa 指针，那么所属 Class 的 isa 是如何规定指向问题的？</p>
</blockquote>
<p>下面引出 <em>元类 meta-class</em> 的概念。</p>
<h2 id="class--isa-meta-class">Class 的 isa 指向：meta-class</h2>
<p>在 Objective-C 中，每一个 Class 都会拥有一个与之相关联的   meta-class 。但是在业务开发中，可能永远不会接触，因为这个 Class 是用来记录一些类信息，而不会直接将其成员的属性接口暴露出来。下面来逐一探究一番（以下例子参考文章 <em><a href="http://www.cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html">What is a meta-class in Objective-C?</a></em> ）：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span><span class="n">viewDidLoad</span> <span class="p">{</span>
    <span class="o">[</span><span class="k">super</span> <span class="n">viewDidLoad</span><span class="o">]</span><span class="p">;</span>
    <span class="no">Class</span> <span class="n">newClass</span> <span class="o">=</span> <span class="n">objc_allocateClassPair</span><span class="p">(</span><span class="o">[</span><span class="no">NSError</span> <span class="n">class</span><span class="o">]</span><span class="p">,</span> <span class="s2">&quot;RuntimeErrorSubclass&quot;</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="n">class_addMethod</span><span class="p">(</span><span class="n">newClass</span><span class="p">,</span> <span class="vi">@selector</span><span class="p">(</span><span class="n">report</span><span class="p">),</span> <span class="p">(</span><span class="no">IMP</span><span class="p">)</span><span class="no">ReportFunction</span><span class="p">,</span> <span class="s2">&quot;v@:&quot;</span><span class="p">);</span>
    <span class="n">objc_registerClassPair</span><span class="p">(</span><span class="n">newClass</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">void</span> <span class="no">ReportFunction</span><span class="p">(</span><span class="nb">id</span> <span class="nb">self</span><span class="p">,</span> <span class="no">SEL</span> <span class="n">_cmd</span><span class="p">)</span> <span class="p">{</span>
    <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;This object is %p.&quot;</span><span class="p">,</span><span class="nb">self</span><span class="p">);</span>
    <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;Class is %@, and super is %@.&quot;</span><span class="p">,</span><span class="o">[</span><span class="nb">self</span> <span class="n">class</span><span class="o">]</span><span class="p">,</span><span class="o">[</span><span class="nb">self</span> <span class="n">superclass</span><span class="o">]</span><span class="p">);</span>
    <span class="no">Class</span> <span class="n">currentClass</span> <span class="o">=</span> <span class="o">[</span><span class="nb">self</span> <span class="n">class</span><span class="o">]</span><span class="p">;</span>
    <span class="k">for</span><span class="p">(</span> <span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">5</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span> <span class="p">)</span> <span class="p">{</span>
        <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;Following the isa pointer %d times gives %p&quot;</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">currentClass</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;NSObject&#39;s class is %p&quot;</span><span class="p">,</span> <span class="o">[</span><span class="no">NSObject</span> <span class="n">class</span><span class="o">]</span><span class="p">);</span>
    <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;NSObject&#39;s meta class is %p&quot;</span><span class="p">,</span><span class="n">object_getClass</span><span class="p">(</span><span class="o">[</span><span class="no">NSObject</span> <span class="n">class</span><span class="o">]</span><span class="p">));</span>
<span class="p">}</span></code></pre></div>
<p>这段代码所做的事情是在 runtime 时期创建 <code>NSError</code> 的一个子类 <code>RuntimeErrorSubclass</code> 。<code>objc_allocateClassPair</code> 方法会创建一个新的 Class ，然后取出 Class 的对象，使用 <code>class_addMethod</code> 方法，为该 Class 添加方法，需要开发者传入添加方法的 Class 、方法名、实现函数、以及定义该函数返回值类型和参数类型的字符串。最后调用 <code>objc_registerClassPair</code> 对其进行注册即可。</p>
<blockquote>
  <p>要点：在调用 <code>objc_allocateClassPair</code> 方法增加新的 Class 的时候，可以调用 <code>class_addIvar</code> 增加成员属性和 <code>objc_registerClassPair</code> 增加成员方法。</p>
</blockquote>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Class</span> <span class="n">objc_allocateClassPair</span><span class="p">(</span><span class="no">Class</span> <span class="n">superclass</span><span class="p">,</span> <span class="n">const</span> <span class="n">char</span> <span class="o">*</span><span class="nb">name</span><span class="p">,</span> 
                             <span class="n">size_t</span> <span class="n">extraBytes</span><span class="p">)</span>
<span class="p">{</span>
    <span class="no">Class</span> <span class="n">cls</span><span class="p">,</span> <span class="n">meta</span><span class="p">;</span>
    <span class="n">rwlock_writer_t</span> <span class="n">lock</span><span class="p">(</span><span class="n">runtimeLock</span><span class="p">);</span>
    <span class="sr">//</span> <span class="err">如果</span> <span class="no">Class</span> <span class="err">名重复则创建失败</span>
    <span class="sr">//</span> <span class="err">如果父类没有通过认证则创建失败</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">getClass</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span>  <span class="o">||</span>  <span class="o">!</span><span class="n">verifySuperclass</span><span class="p">(</span><span class="n">superclass</span><span class="p">,</span> <span class="kp">true</span><span class="o">/*</span><span class="n">rootOK</span><span class="o">*/</span><span class="p">))</span> <span class="p">{</span>
        <span class="k">return</span> <span class="kp">nil</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="sr">//</span> <span class="err">为</span> <span class="n">cls</span> <span class="err">和</span> <span class="n">meta</span> <span class="err">分配空间</span>
    <span class="n">cls</span>  <span class="o">=</span> <span class="n">alloc_class_for_subclass</span><span class="p">(</span><span class="n">superclass</span><span class="p">,</span> <span class="n">extraBytes</span><span class="p">);</span>
    <span class="n">meta</span> <span class="o">=</span> <span class="n">alloc_class_for_subclass</span><span class="p">(</span><span class="n">superclass</span><span class="p">,</span> <span class="n">extraBytes</span><span class="p">);</span>
	<span class="sr">//</span> <span class="err">对</span> <span class="n">cls</span> <span class="err">和</span> <span class="n">meta</span> <span class="err">做指向判定</span>
    <span class="n">objc_initializeClassPair_internal</span><span class="p">(</span><span class="n">superclass</span><span class="p">,</span> <span class="nb">name</span><span class="p">,</span> <span class="n">cls</span><span class="p">,</span> <span class="n">meta</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">cls</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>在 <code>objc_allocateClassPair</code> 方法可以说是 <code>objc_initializeClassPair_internal</code> 的方法入口，其主要的功能是 <strong>根据 superclass 的信息和 Class 中的一些标记成员来确定 cls 和 meta 指针的指向，并调用 <code>addSubclass</code> 方法将其加入到 superclass 中</strong>。</p>
<p>通过 <code>objc_i nitializeClassPair_internal</code> 方法中，调用 <code>meta -&gt; initClassIsa();</code> 来初始化 isa 指针。下面通过 <code>objc_initializeClassPair_internal</code> 来看看 isa 指针和 meta 的初始化方式。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="n">objc_initializeClassPair_internal</span> <span class="err">方法</span>
<span class="sr">//</span> <span class="ss">superclass</span><span class="p">:</span> <span class="err">父类指针</span>
<span class="sr">//</span> <span class="nb">name</span><span class="p">:</span> <span class="err">类名</span>
<span class="sr">//</span> <span class="ss">cls</span><span class="p">:</span> <span class="err">主类索引</span>
<span class="sr">//</span> <span class="ss">meta</span><span class="p">:</span> <span class="n">metaclass</span> <span class="err">索引</span>
<span class="sr">//</span> <span class="err">解锁操作，写操作要求</span>
<span class="n">runtimeLock</span><span class="o">.</span><span class="n">assertWriting</span><span class="p">();</span>
<span class="sr">//</span> <span class="err">只读结构</span> <span class="n">read</span> <span class="n">only</span>
<span class="sr">//</span> <span class="err">分别声明</span> <span class="n">cls</span> <span class="err">和</span> <span class="n">meta</span> <span class="err">两个</span>
<span class="n">class_ro_t</span> <span class="o">*</span><span class="n">cls_ro_w</span><span class="p">,</span> <span class="o">*</span><span class="n">meta_ro_w</span><span class="p">;</span>
<span class="sr">//</span> <span class="err">缓存初始化操作</span>
<span class="n">cls</span><span class="o">-&gt;</span><span class="n">cache</span><span class="o">.</span><span class="n">initializeToEmpty</span><span class="p">();</span>
<span class="n">meta</span><span class="o">-&gt;</span><span class="n">cache</span><span class="o">.</span><span class="n">initializeToEmpty</span><span class="p">();</span>
<span class="sr">//</span> <span class="err">数据设置操作</span>
<span class="sr">//</span> <span class="n">data</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">ro</span> <span class="err">成员，与方法列表，属性，协议相关</span>
<span class="n">cls</span><span class="o">-&gt;</span><span class="n">setData</span><span class="p">((</span><span class="n">class_rw_t</span> <span class="o">*</span><span class="p">)</span><span class="n">calloc</span><span class="p">(</span><span class="n">sizeof</span><span class="p">(</span><span class="n">class_rw_t</span><span class="p">),</span> <span class="mi">1</span><span class="p">));</span>
<span class="n">meta</span><span class="o">-&gt;</span><span class="n">setData</span><span class="p">((</span><span class="n">class_rw_t</span> <span class="o">*</span><span class="p">)</span><span class="n">calloc</span><span class="p">(</span><span class="n">sizeof</span><span class="p">(</span><span class="n">class_rw_t</span><span class="p">),</span> <span class="mi">1</span><span class="p">));</span>
<span class="n">cls_ro_w</span>   <span class="o">=</span> <span class="p">(</span><span class="n">class_ro_t</span> <span class="o">*</span><span class="p">)</span><span class="n">calloc</span><span class="p">(</span><span class="n">sizeof</span><span class="p">(</span><span class="n">class_ro_t</span><span class="p">),</span> <span class="mi">1</span><span class="p">);</span>
<span class="n">meta_ro_w</span>  <span class="o">=</span> <span class="p">(</span><span class="n">class_ro_t</span> <span class="o">*</span><span class="p">)</span><span class="n">calloc</span><span class="p">(</span><span class="n">sizeof</span><span class="p">(</span><span class="n">class_ro_t</span><span class="p">),</span> <span class="mi">1</span><span class="p">);</span>
<span class="n">cls</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">ro</span> <span class="o">=</span> <span class="n">cls_ro_w</span><span class="p">;</span>
<span class="n">meta</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">ro</span> <span class="o">=</span> <span class="n">meta_ro_w</span><span class="p">;</span>
<span class="sr">//</span> <span class="err">进行</span> <span class="n">allocate</span> <span class="err">分配，但没有注册</span>
<span class="c1">#define RW_CONSTRUCTING       (1&lt;&lt;26)</span>
<span class="sr">//</span> <span class="n">ro</span> <span class="err">成员已经</span> <span class="n">copy</span> <span class="err">到</span> <span class="n">heap</span> <span class="err">空间上存储</span>
<span class="c1">#define RW_COPIED_RO          (1&lt;&lt;27)</span>
<span class="sr">//</span> <span class="n">data</span> <span class="err">成员为可读写权限</span>
<span class="c1">#define RW_REALIZED           (1&lt;&lt;31)</span>
<span class="sr">//</span> <span class="err">表示该类已经记录，但尚未实现</span>
<span class="c1">#define RW_REALIZING          (1&lt;&lt;19)</span>
<span class="sr">//</span> <span class="err">进步信息数据操作</span>
<span class="n">cls</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">=</span> <span class="no">RW_CONSTRUCTING</span> <span class="o">|</span> <span class="no">RW_COPIED_RO</span> <span class="o">|</span> <span class="no">RW_REALIZED</span> <span class="o">|</span> <span class="no">RW_REALIZING</span><span class="p">;</span>
<span class="n">meta</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">=</span> <span class="no">RW_CONSTRUCTING</span> <span class="o">|</span> <span class="no">RW_COPIED_RO</span> <span class="o">|</span> <span class="no">RW_REALIZED</span> <span class="o">|</span> <span class="no">RW_REALIZING</span><span class="p">;</span>
<span class="n">cls</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">version</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">meta</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">version</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>
<span class="sr">//</span> <span class="err">表示为</span> <span class="n">metaclass</span> <span class="err">类型</span>
<span class="c1">#define RO_META               (1&lt;&lt;0)</span>
<span class="sr">//</span> <span class="n">cls</span> <span class="err">的</span> <span class="n">flags</span> <span class="err">属性不进行标记</span>
<span class="n">cls_ro_w</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="sr">//</span> <span class="n">meta_ro_w</span> <span class="err">的</span> <span class="n">flags</span> <span class="err">属性进行</span> <span class="n">metaclass</span> <span class="err">类型标记</span>
<span class="n">meta_ro_w</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">=</span> <span class="no">RO_META</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">superclass</span><span class="p">)</span> <span class="p">{</span>
	<span class="sr">//</span> <span class="err">如果没有父类的话，则当前类也为</span> <span class="n">metaclass</span>
   <span class="n">cls_ro_w</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">|=</span> <span class="no">RO_ROOT</span><span class="p">;</span>
   <span class="n">meta_ro_w</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">|=</span> <span class="no">RO_ROOT</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">superclass</span><span class="p">)</span> <span class="p">{</span>
	<span class="sr">//</span> <span class="err">有无父类情况，传递</span> <span class="n">instanceStart</span>
   <span class="n">cls_ro_w</span><span class="o">-&gt;</span><span class="n">instanceStart</span> <span class="o">=</span> <span class="n">superclass</span><span class="o">-&gt;</span><span class="n">unalignedInstanceSize</span><span class="p">();</span>
   <span class="n">meta_ro_w</span><span class="o">-&gt;</span><span class="n">instanceStart</span> <span class="o">=</span> <span class="n">superclass</span><span class="o">-&gt;</span><span class="no">ISA</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">unalignedInstanceSize</span><span class="p">();</span>
   <span class="n">cls</span><span class="o">-&gt;</span><span class="n">setInstanceSize</span><span class="p">(</span><span class="n">cls_ro_w</span><span class="o">-&gt;</span><span class="n">instanceStart</span><span class="p">);</span>
   <span class="n">meta</span><span class="o">-&gt;</span><span class="n">setInstanceSize</span><span class="p">(</span><span class="n">meta_ro_w</span><span class="o">-&gt;</span><span class="n">instanceStart</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
   <span class="n">cls_ro_w</span><span class="o">-&gt;</span><span class="n">instanceStart</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
   <span class="n">meta_ro_w</span><span class="o">-&gt;</span><span class="n">instanceStart</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint32_t</span><span class="p">)</span><span class="n">sizeof</span><span class="p">(</span><span class="n">objc_class</span><span class="p">);</span>
   <span class="n">cls</span><span class="o">-&gt;</span><span class="n">setInstanceSize</span><span class="p">((</span><span class="n">uint32_t</span><span class="p">)</span><span class="n">sizeof</span><span class="p">(</span><span class="nb">id</span><span class="p">));</span>
   <span class="n">meta</span><span class="o">-&gt;</span><span class="n">setInstanceSize</span><span class="p">(</span><span class="n">meta_ro_w</span><span class="o">-&gt;</span><span class="n">instanceStart</span><span class="p">);</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">记录</span> <span class="no">Class</span> <span class="err">名</span>
<span class="n">cls_ro_w</span><span class="o">-&gt;</span><span class="nb">name</span> <span class="o">=</span> <span class="n">strdup</span><span class="p">(</span><span class="nb">name</span><span class="p">);</span>
<span class="n">meta_ro_w</span><span class="o">-&gt;</span><span class="nb">name</span> <span class="o">=</span> <span class="n">strdup</span><span class="p">(</span><span class="nb">name</span><span class="p">);</span>
<span class="sr">//</span> <span class="err">属性修饰符布局</span>
<span class="sr">//</span> <span class="n">ivarLayout</span> <span class="n">strong</span><span class="err">引用表</span>
<span class="n">cls_ro_w</span><span class="o">-&gt;</span><span class="n">ivarLayout</span> <span class="o">=</span> <span class="o">&amp;</span><span class="no">UnsetLayout</span><span class="p">;</span>
<span class="sr">//</span> <span class="n">weakIvarLayout</span> <span class="n">weak</span><span class="err">引用表</span>
<span class="n">cls_ro_w</span><span class="o">-&gt;</span><span class="n">weakIvarLayout</span> <span class="o">=</span> <span class="o">&amp;</span><span class="no">UnsetLayout</span><span class="p">;</span>
<span class="sr">//</span> <span class="err">通过获取到的</span> <span class="n">cls</span> <span class="err">指针，调用</span> <span class="n">isa</span> <span class="err">初始化命令</span>
<span class="n">cls</span><span class="o">-&gt;</span><span class="n">initClassIsa</span><span class="p">(</span><span class="n">meta</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">superclass</span><span class="p">)</span> <span class="p">{</span>
	<span class="sr">//</span> <span class="err">如果拥有父类，更新</span> <span class="n">meta</span> <span class="err">的</span> <span class="n">isa</span> <span class="err">指向</span>
   <span class="n">meta</span><span class="o">-&gt;</span><span class="n">initClassIsa</span><span class="p">(</span><span class="n">superclass</span><span class="o">-&gt;</span><span class="no">ISA</span><span class="p">()</span><span class="o">-&gt;</span><span class="no">ISA</span><span class="p">());</span>
   <span class="sr">//</span> <span class="err">更新</span> <span class="n">cls</span> <span class="err">父类信息</span>
   <span class="n">cls</span><span class="o">-&gt;</span><span class="n">superclass</span> <span class="o">=</span> <span class="n">superclass</span><span class="p">;</span>
   <span class="sr">//</span> <span class="n">meta</span> <span class="err">的父类指向父类的</span> <span class="n">isa</span>
   <span class="n">meta</span><span class="o">-&gt;</span><span class="n">superclass</span> <span class="o">=</span> <span class="n">superclass</span><span class="o">-&gt;</span><span class="no">ISA</span><span class="p">();</span>
   <span class="sr">//</span> <span class="err">向父类中增加该类信息</span>
   <span class="n">addSubclass</span><span class="p">(</span><span class="n">superclass</span><span class="p">,</span> <span class="n">cls</span><span class="p">);</span>
   <span class="sr">//</span> <span class="err">向父类的</span> <span class="n">isa</span> <span class="err">中记录该信息</span>
   <span class="n">addSubclass</span><span class="p">(</span><span class="n">superclass</span><span class="o">-&gt;</span><span class="no">ISA</span><span class="p">(),</span> <span class="n">meta</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
	<span class="sr">//</span> <span class="err">为</span> <span class="n">meta</span> <span class="err">初始化</span> <span class="n">isa</span> <span class="err">信息</span>
   <span class="n">meta</span><span class="o">-&gt;</span><span class="n">initClassIsa</span><span class="p">(</span><span class="n">meta</span><span class="p">);</span>
   <span class="sr">//</span> <span class="err">由于该类为</span> <span class="n">rootclass</span><span class="err">，无父类信息</span>
   <span class="sr">//</span> <span class="err">让其父类指向</span> <span class="no">Nil</span>
   <span class="n">cls</span><span class="o">-&gt;</span><span class="n">superclass</span> <span class="o">=</span> <span class="no">Nil</span><span class="p">;</span>
   <span class="sr">//</span> <span class="err">令</span> <span class="n">meta</span> <span class="err">的父类指向</span> <span class="n">cls</span>
   <span class="n">meta</span><span class="o">-&gt;</span><span class="n">superclass</span> <span class="o">=</span> <span class="n">cls</span><span class="p">;</span>
   <span class="sr">//</span> <span class="err">向</span> <span class="n">cls</span> <span class="err">中增加</span> <span class="n">meta</span> <span class="err">指针信息</span>
   <span class="n">addSubclass</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">meta</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p>在语法上需要注意这几个地方：</p>
<ul>
  <li>ivarLayout 和 weakIvarLayout：分别记录了哪些 ivar 是 strong 或是 weak，都未记录则为 __unsafe_unretained 的对象类型。</li>
  <li><code>strdup(const char *s)</code>：可以复制字符串。先回调用 malloc() 配置与参数 s 字符串的内容复制到该内存地址，然后把该地址返回。返回值是一个字符串指针，该指针指向复制后的新字符串地址。若返回 NULL 表示内存不足。</li>
</ul>
<p>在上述代码中，会发现一个问题。当创建的 Class 没有父类的时候，其 meta 是指向 cls 自身的，而 meta 原本就是 cls 的子类，所以在这里，使得一个基类对象的 isa 指针形成自环指向自身。下图用 <code>NSObject</code> 举例（其指针下方有源码标注）：</p>
<p><img src="http://7xwh85.com1.z0.glb.clouddn.com/isa_obj_x.png" alt="isa_obj_x2" /></p>
<p>而当创建 Class 拥有父类的时候，isa 和 superclass 都要指向父类，而对应的 meta 通过两次的 isa 查询找到根类 meta ，更新指向。用 <code>NSError</code> 来举例：</p>
<p><img src="http://7xwh85.com1.z0.glb.clouddn.com/isa_metaclass.png" alt="isa_obj_x" /></p>
<p>其中要之一 meta 的 isa 操作 <code>meta-&gt;initClassIsa(superclass-&gt;ISA()-&gt;ISA());</code> ，这不是单纯的指向父类 meta 的操作，而是指向根类的 meta 。</p>
<p><em>Talk is cheap!</em> ，用代码来实验一下：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span><span class="n">viewDidLoad</span> <span class="p">{</span>
    <span class="o">[</span><span class="k">super</span> <span class="n">viewDidLoad</span><span class="o">]</span><span class="p">;</span>
    <span class="no">DGObject</span> <span class="o">*</span><span class="n">desgard</span> <span class="o">=</span> <span class="o">[[</span><span class="no">DGObject</span> <span class="n">alloc</span><span class="o">]</span> <span class="n">init</span><span class="o">]</span><span class="p">;</span>
    <span class="no">Class</span> <span class="n">cls</span> <span class="o">=</span> <span class="n">object_getClass</span><span class="p">(</span><span class="n">desgard</span><span class="p">);</span>
    <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;%s</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">class_getName</span><span class="p">(</span><span class="n">cls</span><span class="p">));</span> <span class="sr">//</span> <span class="no">DGObject</span>
    <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;%d</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">class_isMetaClass</span><span class="p">(</span><span class="n">cls</span><span class="p">));</span> <span class="sr">//</span> <span class="mi">0</span>
    <span class="no">Class</span> <span class="n">meta</span> <span class="o">=</span> <span class="n">object_getClass</span><span class="p">(</span><span class="n">cls</span><span class="p">);</span>
    <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;%s</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">class_getName</span><span class="p">(</span><span class="n">meta</span><span class="p">));</span> <span class="sr">//</span> <span class="no">DGObject</span>
    <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;%d</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">class_isMetaClass</span><span class="p">(</span><span class="n">cls</span><span class="p">));</span> <span class="sr">//</span> <span class="mi">0</span>
    <span class="no">Class</span> <span class="n">meta_meta</span> <span class="o">=</span> <span class="n">object_getClass</span><span class="p">(</span><span class="n">meta</span><span class="p">);</span>
    <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;%s</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">class_getName</span><span class="p">(</span><span class="n">meta_meta</span><span class="p">));</span> <span class="sr">//</span> <span class="no">NSObject</span>
    <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;%d</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">class_isMetaClass</span><span class="p">(</span><span class="n">meta_meta</span><span class="p">));</span> <span class="sr">//</span> <span class="mi">1</span>
<span class="p">}</span></code></pre></div>
<p>通过以上分析，我们知道了 metaclass 是一个 Class ，而这个 Class 是作为基础 Class 的所属类，用于构建<strong>继承网图</strong>，使得 runtime 访问相关联的 Class 更加的快捷方便。在 <em><a href="">What is a meta-class in Objective-C?</a></em> 一文中，作者将其称作 <strong>NSObject继承体系(NSObject hierarchy)</strong> ，其根类所有的 Class 和相关 metaclass 都是联通的，并且在根类 NSObject 中的成员方法，对其体系中的所有 Class 和对应 metaclass 也是操作有效的。</p>
<p>metaclass 的存在，将对象化的实例、类组织成了一个连通图，进一步灵活了 ObjC 的动态特性。</p>
<p>至此，我们通过源码，系统了解了 isa 指针对于对象的信息记录，以及 metaclass 的结构和作用。后续博文将会探究 <code>retain</code> 和 <code>release</code> 方法，敬请期待。</p>
  <footer class="post-footer">
    <section class="author">
      <h4>Desgard_Duan</h4>
      <p>I write many code to write less code.💻</p>
    </section>
<aside class="share">
  <h4>Share this.</h4>
  <a href="http://twitter.com/share?text=《用 isa 承载对象的类信息》 -- Guardia · 瓜地&amp;url=https://www.desgard.com/isa/"
  onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
    <i class="fa fa-twitter-square"></i>
  </a>
  <a href="http://v.t.sina.com.cn/share/share.php?url=https://www.desgard.com/isa/&amp;title=《用 isa 承载对象的类信息》 —— Guardia · 瓜地" onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
    <i class="fa fa-weibo"></i>
  </a>
</aside>
      <hr>
<aside id="comments" class="disqus">
  <div id="container"></div>
  <!-- <link rel="stylesheet" href="/assets/css/gitment.css">
  <script src="https://imsun.github.io/gitment/dist/gitment.browser.js"></script>
  <script>
  var gitment = new Gitment({
    id: "https://www.desgard.com/isa/", 
    owner: 'Desgard',
    repo: 'desgard.github.com',
    oauth: {
      client_id: 'e2612df42f3f2a83e71c',
      client_secret: 'b53e85b314bb24a6d06773e48bbb62a4de3b8b3a',
    },
  })
  gitment.render('container')
  </script> -->
<link rel="stylesheet" href="/assets/css/gitalk.css">
<script src="https://unpkg.com/gitalk/dist/gitalk.min.js"></script>
<div id='gitalk-container'></div>
<script>
    const gitalk = new Gitalk({
        id: "https://www.desgard.com/isa/", 
        clientID: 'e2612df42f3f2a83e71c',
        clientSecret: 'b53e85b314bb24a6d06773e48bbb62a4de3b8b3a',
        repo: 'desgard.github.com',
        owner: 'Desgard',
        admin: ['Desgard'],
        // facebook-like distraction free mode
        distractionFreeMode: false
    })
    gitalk.render('gitalk-container')
</script>
</aside>
  </footer>
</article>
    </main>
<footer class="footer">
  <div class="container">
    <ul class="icons">
      <li>
        <a href="https://github.com/desgard" class="icon-github" target="_blank">
          <i class="fa fa-github"></i>
        </a>
      </li>
      <li>
        <a href="https://www.facebook.com/desgard.duan" class="icon-facebook" target="_blank">
          <i class="fa fa-facebook"></i>
        </a>
      </li>
      <li>
        <a href="https://twitter.com/Desgard_Duan" class="icon-twitter" target="_blank">
          <i class="fa fa-twitter"></i>
        </a>
      </li>
      <li>
        <a href="https://stackoverflow.com/users/6119149/desgard-duan" class="icon-github" target="_blank">
          <i class="fa fa-stack-overflow"></i>
        </a>
      </li>
      <li>
        <a href="https://weibo.com/desgard" class="icon-instagram" target="_blank">
          <i class="fa fa-weibo"></i>
        </a>
      </li>
    </ul>
    <p>
      <q>I write many code to write less code.💻</q>
      <small>– Gua</small>
    </p>
    <small class="clearfix">
      Powered by <a href="http://jekyllrb.com" target="_blank">Jekyll</a> • <a href="https://github.com/desgard" target="_blank">Open source <i class="fa fa-heart"></i></a>
    </small>
  </div>
</footer>
<a class="scroll-up fa fa-chevron-up bounce" href="#" data-position="0"></a>
<div id="modalSearch" class="modal">
  <div class="modal__overlay"></div>
  <div class="modal__content">
    <a href="#!" class="modal-close" data-modal-close>&times;</a>
    <div class="search-container">
      <input type="text" id="search-input" placeholder="Search articles">
      <ul id="results-container"></ul>
    </div>
  </div>
</div>
    <script src="/assets/main-52d417e8a6ff9f5b168386d37c96338a.js"></script>
  </body>
  <script>
    var link = "" ;
    var os = function() {  
      var ua = navigator.userAgent,  
      isWindowsPhone = /(?:Windows Phone)/.test(ua),  
      isSymbian = /(?:SymbianOS)/.test(ua) || isWindowsPhone,   
      isAndroid = /(?:Android)/.test(ua),   
      isFireFox = /(?:Firefox)/.test(ua),   
      isChrome = /(?:Chrome|CriOS)/.test(ua),  
      isTablet = /(?:iPad|PlayBook)/.test(ua) || (isAndroid && !/(?:Mobile)/.test(ua)) || (isFireFox && /(?:Tablet)/.test(ua)),  
      isPhone = /(?:iPhone)/.test(ua) && !isTablet,  
      isPc = !isPhone && !isAndroid && !isSymbian;  
      return {  
        isTablet: isTablet,  
        isPhone: isPhone,  
        isAndroid : isAndroid,  
        isPc : isPc  
      };  
    }();  
    if (link.length > 0) {
      if (os.isAndroid || os.isPhone) {
        location.replace(link);
      }
    }
  </script>
</html>